package com.lhkj.ct.framework.shiro.credential;

import com.lhkj.ct.base.cache.redis.RedisAccess;
import com.lhkj.ct.base.utils.MessageUtils;
import com.lhkj.ct.framework.shiro.token.UsernamePasswordAuthToken;
import com.lhkj.ct.meta.admin.service.SysConfigService;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.DisabledAccountException;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author 杨郑耀
 * @description
 * @create 2019-06-14-9:26
 */
public class RetryLimitHashedCredentialsMatcher extends HashedCredentialsMatcher {

	private static final Logger log = LoggerFactory.getLogger(RetryLimitHashedCredentialsMatcher.class);

	@Resource
	private RedisAccess redisAccess;

	@Resource
	private SysConfigService sysConfigService;

	private static final String LIMIT_KEY_PREFIX = "shiro:cache:retry:limit:";

	private static final String EXCEED_KEY_PREFIX = "shiro:cache:retry:exceed:";

	public RetryLimitHashedCredentialsMatcher() {
	}

	@Override
	public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
		// 如果是免密登录，则直接登录
		if (token instanceof UsernamePasswordAuthToken && ((UsernamePasswordAuthToken) token).isNoPassword()) {
			return true;
		}
		int limit = sysConfigService.selectPwdRetryLimit();
		//1.通过令牌获取用户名
		String username = (String)token.getPrincipal();
		long exceedTime = 0;
		if ((exceedTime = redisAccess.getExpire(getExceedCacheKey(username), TimeUnit.MINUTES)) > 0) {
			throw new DisabledAccountException(MessageUtils.message("user.password.retry.limit.tips", username, exceedTime));
		}
		//2.判断缓存中是否包含含有此用户名的AtomicInteger
		AtomicInteger retryCount = redisAccess.getCacheObject(getLimitCacheKey(username));
		//3.如果没有 ,就创建包含该用户名的Element并添加到缓存
		if (retryCount == null) {
			// 如果用户没有登陆过,登陆次数加1 并放入缓存
			retryCount = new AtomicInteger(0);
		}
		//4.如果AutomicInteger的incrementAndGet大于系统设置次数 就抛异常
		if (limit > 0 && retryCount.incrementAndGet() > limit){
			int exceed = sysConfigService.selectPwdRetryLimit();
			redisAccess.setCacheObject(getExceedCacheKey(username), username,exceed, TimeUnit.MINUTES);
			throw new AuthenticationException(MessageUtils.message("user.password.retry.limit.exceed", limit, exceed));
		}
		//6.如果小于设置的次数 就调用父类的doCredentialsMatch方法进行密码验证
		boolean matchs = super.doCredentialsMatch(token, info);
		//7.如果验证通过，就将该username从缓存中清除
		if (matchs){
			redisAccess.deleteObject(getLimitCacheKey(username));
		}else{
			redisAccess.setCacheObject(getLimitCacheKey(username), retryCount);
		}
		return matchs;
	}

	public String getLimitCacheKey(String key) {
		return LIMIT_KEY_PREFIX + key;
	}

	public String getExceedCacheKey(String key) {
		return EXCEED_KEY_PREFIX + key;
	}


}
